<template>
  <div class="game-board" @keyup="handleKeyUp">
    <!-- (div.board-row>(div.board-block>(div.block-row>(GameBoardCeil[:style="ceilSize" num=$]*3))*3)*3)*3 -->
    <div class="sudoku-timer">
      <h3>Time Use</h3>
      <GameTime></GameTime>
    </div>
    <table class="sudoku-board">
      <tr v-for="(row, rowIdx) in sudokuState" :key="rowIdx">
        <GameBoardCeil
          v-for="(val, colIdx) in row"
          :key="colIdx"
          class="sudoku-board__ceil"
          :mainValue="val.mainValue"
          :candidates="val.candidates"
          :color="val.color"
          :rowIdx="rowIdx"
          :colIdx="colIdx"
          @click="handleClickCeil(rowIdx, colIdx)"
        />
      </tr>
    </table>

    <div class="sudoku-control__input">
      <RadioGroup v-model="inputMode" type="button">
        <Radio label="Main">主选</Radio>
        <Radio label="Candidate">候选</Radio>
      </RadioGroup>
      <RadioGroup v-model="controlMode" type="button">
        <Radio label="Input">选择</Radio>
        <Radio label="Delete">删除</Radio>
      </RadioGroup>
    </div>

    <div class="sudoku-select">
      <RadioGroup class="sudoku-select__group" v-model="inputNum" type="button">
        <Radio v-for="num in 9" :key="num" :label="num"></Radio>
        <Radio label="数字选择" disabled />
      </RadioGroup>
    </div>

    <div class="control-btn">
      <Button type="success" @click="answerSubmited">提交</Button>
      <Button @click="handleClickUndo">上一步</Button>
      <Button @click="handleClickRedo">下一步</Button>
      <Button type="error" @click="handleClickClear">清空</Button>
      <Button @click="handleClickSave">保存进度</Button>
      <Button @click="handleClickLoad" :disabled="!canAnswerLoad"
        >读取进度</Button
      >
    </div>
  </div>
</template>

<script lang="ts">
import { Component, Prop, Vue } from '@/plugins/class-component-hooks'
import GameBoardCeil from '@/components/GameBoardCeil.vue'
import GameTime from '@/components/GameTime.vue'
import { Time } from 'view-design'
import { SudokuGame } from '../util/sudoku/game'
import {
  InputCertainNumCmd,
  Command,
  CmdParam,
  InputCandidateNumCmd,
  DeleteCertainNumCmd,
  DeleteCandidateNumCmd
} from '../util/sudoku/command'
import { SudokuCeilValue, Sudoku } from '../util/sudoku/sudoku'
import { Watch, Emit } from 'vue-property-decorator'
import db from '../util/db'

// keyLocalAnswer is use to query from local storage
// To implement the feature Save/Load
function keyLocalAnswer(puzzleID: string) {
  return `ANSWER_PUZZLE_${puzzleID}`
}

function loadLocalAnswer(puzzleID: string) {
  return db.get<string>(keyLocalAnswer(puzzleID), () => '')
}

function saveLocalAnswer(puzzleID: string, ans: string) {
  db.save<string>(keyLocalAnswer(puzzleID), ans)
}

enum InputMode {
  Main = 'Main',
  Candidate = 'Candidate'
}

enum ControlMode {
  Delete = 'Delete',
  Input = 'Input'
}

@Component({
  components: {
    GameBoardCeil,
    GameTime
  }
})
export default class GameBoard extends Vue {
  @Prop({ default: 1000000 }) private readonly remainTime!: number
  @Prop({ default: '' }) private readonly puzzle!: string
  @Prop({ default: '' }) private readonly conflict!: string
  @Prop() private readonly puzzleID!: string

  private sudokuGame!: SudokuGame
  private sudokuState!: SudokuCeilValue[][]
  private inputNum: number = 2
  private inputMode: InputMode = InputMode.Main
  private controlMode: ControlMode = ControlMode.Input
  private duration: number = 0
  // whether sudoku can be loaded from storage
  private canAnswerLoad: boolean = false

  private cmdFactoryMap: Record<
    ControlMode,
    Record<InputMode, (param: CmdParam) => Command>
  > = {
    Input: {
      Main: param => new InputCertainNumCmd(param),
      Candidate: param => new InputCandidateNumCmd(param)
    },
    Delete: {
      Main: param => new DeleteCertainNumCmd(param),
      Candidate: param => new DeleteCandidateNumCmd(param)
    }
  }

  // Use any to avoid interval diffrent type warning
  // In Node js and browswer environment
  private intervalId: any

  mounted() {
    this.intervalId = setInterval(() => this.duration++, 1000)

    // Load game if ok
    this.handleClickLoad()
  }

  destroy() {
    clearInterval(this.intervalId)
  }

  constructor() {
    super()

    const n = 9
    this.sudokuState = Sudoku.NewDisplayState()
    this.sudokuGame = new SudokuGame(this.sudokuState)
  }

  // Init sudoku state when prop change
  @Watch('puzzle', { immediate: true })
  onPuzzleUpdated() {
    // if have valid string
    if (this.puzzle !== '') {
      this.sudokuGame.SetPuzzle(this.puzzle)
    }
  }

  @Watch('conflict')
  onconflictUpdated() {
    if (this.conflict !== '') {
      this.sudokuGame.SetConflictElem(this.conflict)
    }
  }

  @Emit('answerSubmited')
  answerSubmited() {
    return this.sudokuGame.AnswerStr
  }

  get ceilSize() {
    const width = '40px'
    return {
      width: width,
      height: width
    }
  }

  handleClickCeil(rowIdx: number, colIdx: number) {
    let cmdFn = this.cmdFactoryMap[this.controlMode][this.inputMode]
    let cmd = cmdFn({
      rowIdx,
      colIdx,
      value: this.inputNum
    })

    this.sudokuGame.execute(cmd)
  }

  handleClickUndo() {
    this.sudokuGame.Undo()
  }

  handleClickRedo() {
    this.sudokuGame.Redo()
  }

  handleClickClear() {
    this.sudokuGame.Clear()
  }

  handleClickSave() {
    let ans = this.sudokuGame.AnswerStr
    if (ans == '') {
      // ignore
      return
    }
    saveLocalAnswer(this.puzzleID, ans)
    this.canAnswerLoad = true
  }

  handleClickLoad() {
    let ans = loadLocalAnswer(this.puzzleID)
    if (ans == '') {
      // ignore
      return
    }
    // load
    this.sudokuGame.SetAnswer(ans)
    // Can be loaded
    this.canAnswerLoad = true
  }

  handleKeyUp(e: KeyboardEvent) {
    if (e.defaultPrevented) {
      return
    }
    let num = parseInt(e.key)
    if (num >= 0 && num <= 9) {
      this.inputNum = num
      e.preventDefault()
      return
    }

    // LeftArrow or RightArrow
    if (e.key == 'ArrowLeft') {
      this.inputNum = (this.inputNum - 1 + 10) % 10
      e.preventDefault()
      return
    }
    // rightArrow
    if (e.key == 'ArrowRight') {
      this.inputNum = (this.inputNum + 1) % 10
      e.preventDefault()
      return
    }
  }
}
</script>

<!-- Add "scoped" attribute to limit CSS to this component only -->
<style scoped lang="scss">
@import '@/styles/general.scss';
$border-color: salmon;
.game-board {
  display: flex;
  flex-direction: column;
  align-items: center;
  .sudoku-timer {
    text-align: center;
  }

  .sudoku-board {
    border: 4px solid $border-color;
    border-collapse: collapse;
    margin-top: 1.2rem;
    margin-bottom: 2rem;

    td {
      border: 1px solid $border-color;

      &:nth-child(3n + 3) {
        border-right: 4px solid $border-color;
      }
    }

    tr:nth-child(3n + 3) {
      border-bottom: 4px solid $border-color;
    }

    &__ceil {
      width: 2.6rem;
      height: 2.6rem;
      font-size: 1rem;
      @media screen and (max-width: $size-md) {
        width: 2.4rem;
        height: 2.4rem;
        font-size: 1rem;
      }
      @media screen and (max-width: $size-sm) {
        width: 1.8rem;
        height: 1.8rem;
        font-size: 0.8rem;
      }
      @media screen and (max-width: $size-xs) {
        width: 1.5rem;
        height: 1.5rem;
        font-size: 0.8rem;
      }
    }
  }

  .sudoku-select {
    width: 100%;
    display: flex;
    justify-content: center;

    &__group {
      width: 100%;
      display: flex;
      justify-content: center;
      flex-wrap: wrap;
    }
  }
}

.control-btn {
  width: 80%;
  display: grid;
  margin-top: 2rem;

  grid-template-columns: 1fr 1fr 1fr;
  grid-column-gap: 0.8rem;
  grid-row-gap: 0.5rem;

  @media screen and (max-width: $size-md) {
    grid-template-columns: 1fr 1fr;
  }

  @media screen and (max-width: $size-xsm) {
    grid-template-columns: 1fr;
  }

  button {
    width: 100%;
  }

  .btn-row {
    margin-top: 0.5rem;
  }
}

.sudoku-control__input {
  width: 100%;
  display: flex;
  margin-bottom: 1.2rem;
  justify-content: center;

  > :first-child {
    margin-right: 1rem;
  }

  > div.ivu-radio-group {
    display: flex;
    justify-content: center;
  }

  @media screen and (max-width: $size-md) {
    flex-direction: column;
    align-items: center;
    justify-content: center;
    > :first-child {
      margin-right: 0;
      margin-bottom: 0.8rem;
    }
  }
}
</style>
